/*
 * Copyright 2011 www.pretty-tools.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "stdafx.h"
#include "FilePicker.h"
#include <atldlgs.h>

#define     modeOpen  	        0
#define 	modeSave 	        1
#define 	modeGetFolder 	    2
#define 	modeOpenMultiple 	3

#define 	returnOK 	    0
#define 	returnCancel 	1
#define 	returnReplace 	2

#define 	filterAll 	    1
#define 	filterHTML 	    2
#define 	filterText 	    4
#define 	filterImages 	8
#define 	filterXML 	   16
#define 	filterXUL 	   32
#define 	filterApps 	   64

#define MAX_FILES	1000
#define BUFFER_SIZE (MAX_FILES * (MAX_PATH+1))

// CFilePicker

CFilePicker::CFilePicker()
    : m_nMode(0)
    , m_nFilterIndex(0)
    , m_szFileNames(NULL)
    , m_nFileOffset(0)
{
    ATLTRACE( _T("CFilePicker::CFilePicker()\n") );
}

CFilePicker::~CFilePicker()
{
    ATLTRACE( _T("CFilePicker::~CFilePicker()\n") );
    delete [] m_szFileNames;
}


STDMETHODIMP CFilePicker::init(IDispatch* parent, BSTR title, LONG mode)
{
    if (!title || mode < modeOpen || mode > modeOpenMultiple)
        return E_INVALIDARG;

    m_strFilter.Empty();
    m_strDefaultExtension.Empty();
    m_strDefaultString.Empty();
    m_strDisplayDirectory.Empty();
    m_nFilterIndex = 0;

    if (!(m_wndParent = HTML::Window(parent).top.hwnd))
        m_wndParent = ::GetActiveWindow();
    m_strTitle = title;
    m_nMode    = mode;

    return S_OK;
}

STDMETHODIMP CFilePicker::appendFilter(BSTR title, BSTR filter)
{
    m_strFilter += title;
    m_strFilter += _T("|");
    m_strFilter += filter;
    m_strFilter += _T("|");

    return S_OK;
}

STDMETHODIMP CFilePicker::appendFilters(LONG filterMask)
{
    if (filterMask & filterAll)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_ALL);
    if (filterMask & filterHTML)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_HTML);
    if (filterMask & filterText)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_TEXT);
    if (filterMask & filterImages)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_IMAGES);
    if (filterMask & filterXML)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_XML);
    if (filterMask & filterXUL)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_XUL);
    if (filterMask & filterApps)
        m_strFilter += GetString(IDS_FILE_PICKER_FILTER_APPS);

    return S_OK;
}

STDMETHODIMP CFilePicker::show(LONG* pRet)
{
    if (!pRet)
        return E_POINTER;

    if (m_nMode != modeGetFolder)
    {
        BOOL bOpenFileDialog = (modeSave != m_nMode);
        DWORD dwFlags = OFN_HIDEREADONLY;
        if (bOpenFileDialog)
            dwFlags |= OFN_FILEMUSTEXIST;
        else
            dwFlags |= OFN_OVERWRITEPROMPT;
        if (modeOpenMultiple == m_nMode)
            dwFlags |= OFN_ALLOWMULTISELECT;

        CString strFilter = m_strFilter;
		if (!strFilter.IsEmpty())
			strFilter.Replace( _T('|'), _T('\00') );

        CFileDialog dlg(bOpenFileDialog, StringOrNULL(m_strDefaultExtension), StringOrNULL(m_strDefaultString), dwFlags, StringOrNULL(strFilter), m_wndParent);
        dlg.m_ofn.lpstrTitle = StringOrNULL(m_strTitle);
        dlg.m_ofn.lpstrInitialDir = StringOrNULL(m_strDisplayDirectory);
        dlg.m_ofn.nFilterIndex = m_nFilterIndex;

        if (!m_szFileNames)
            m_szFileNames = new TCHAR[BUFFER_SIZE];

        ::ZeroMemory(m_szFileNames, BUFFER_SIZE * sizeof(TCHAR));
        dlg.m_ofn.lpstrFile = m_szFileNames;
		dlg.m_ofn.nMaxFile  = BUFFER_SIZE;
        //copy default file name
        _tcscpy_s(dlg.m_ofn.lpstrFile, BUFFER_SIZE, dlg.m_szFileName);

        int nResult = dlg.DoModal();
        if (IDCANCEL == nResult)
            *pRet = returnCancel;
        else
        {
            ATLASSERT(IDOK == nResult);

            *pRet = returnOK;
            if (!bOpenFileDialog && File::Exists(m_szFileNames))
                *pRet = returnReplace;
        }

        m_nFilterIndex = dlg.m_ofn.nFilterIndex;
        m_nFileOffset = dlg.m_ofn.nFileOffset;
    }
    else
    {
        m_strTitle.Replace( _T("\\n"), _T("\n") );

        CFolderDialog dlg(m_wndParent, StringOrNULL(m_strTitle), BIF_NEWDIALOGSTYLE|BIF_RETURNONLYFSDIRS);

        if (!m_strDisplayDirectory.IsEmpty())
            dlg.SetInitialFolder(m_strDisplayDirectory);

        if (IDOK == dlg.DoModal())
        {
            *pRet = returnOK;

            if (!m_szFileNames)
                m_szFileNames = new TCHAR[BUFFER_SIZE];

            ::ZeroMemory(m_szFileNames, BUFFER_SIZE * sizeof(TCHAR));
            _tcscpy_s(m_szFileNames, BUFFER_SIZE, dlg.GetFolderPath());
        }
        else
        {
            *pRet = returnCancel;

            if (m_szFileNames)
                m_szFileNames[0] = 0;
        }
    }

    return S_OK;
}

STDMETHODIMP CFilePicker::get_file(ILocalFile** pVal)
{
    if (!pVal)
        return E_POINTER;

    if (modeOpenMultiple == m_nMode)
        return E_UNEXPECTED;
    if (!m_szFileNames || !*m_szFileNames)
        return E_UNEXPECTED;

    CComPtr<ILocalFile> pFile;
    RETURN_FAILED(pFile.CoCreateInstance(CLSID_LocalFile))
    RETURN_FAILED(pFile->initWithNativePath(CComBSTR(m_szFileNames)))
    
    CComQIPtr<ISafeLocalFile> pSafeFile = pFile;
    if (pSafeFile) pSafeFile->SetSafe(VARIANT_TRUE);

    *pVal = pFile.Detach();

    return S_OK;
}

ULONG CFilePicker::GetFileCount(void)
{
    if (!m_szFileNames)
        return 0;

	ULONG uNumFiles = 0;
	for (LPCTSTR file = m_szFileNames; *file; file += _tcslen(file)+1)
		++uNumFiles;

	return uNumFiles == 1 ? 1 : uNumFiles - 1;
}

STDMETHODIMP CFilePicker::get_files(ISimpleEnumerator** pVal)
{
   if (!pVal)
        return E_POINTER;
    if (modeOpenMultiple != m_nMode)
        return E_UNEXPECTED;

    ULONG uCount = GetFileCount();
    ATLTRACE( _T("CFilePicker::get_files() - %i elements\n"), uCount);

    CComPtr<ISimpleEnumerator> enumerator;
    RETURN_FAILED(enumerator.CoCreateInstance(CLSID_SimpleEnumerator))

    CComSafeArray<VARIANT> arValues;
    RETURN_FAILED(arValues.Create(uCount))

    int iPos = 0;
    for (Iterator it(*this); (bool)it; ++it)
    {
        arValues[iPos++] = *it;
    }

    CComVariant vtValues;
    vtValues.vt = VT_ARRAY | VT_VARIANT;
	vtValues.parray = arValues.Detach();

    RETURN_FAILED(enumerator->Init(vtValues))

    *pVal = enumerator.Detach();

    return S_OK;
}

STDMETHODIMP CFilePicker::get_defaultExtension(BSTR* pVal)
{
    if (!pVal)
        return E_POINTER;

    *pVal = CComBSTR(m_strDefaultExtension).Detach();

    return S_OK;
}

STDMETHODIMP CFilePicker::put_defaultExtension(BSTR newVal)
{
    if (!newVal)
        return E_INVALIDARG;

    m_strDefaultExtension = newVal;

    return S_OK;
}

STDMETHODIMP CFilePicker::get_defaultString(BSTR* pVal)
{
    if (!pVal)
        return E_POINTER;

    *pVal = CComBSTR(m_strDefaultString).Detach();

    return S_OK;
}

STDMETHODIMP CFilePicker::put_defaultString(BSTR newVal)
{
    if (!newVal)
        return E_INVALIDARG;

    m_strDefaultString = newVal;

    return S_OK;
}

STDMETHODIMP CFilePicker::get_displayDirectory(ILocalFile** pVal)
{
    if (!pVal)
        return E_POINTER;

    return E_NOTIMPL;
}

STDMETHODIMP CFilePicker::put_displayDirectory(ILocalFile* newVal)
{
    if (!newVal)
        return E_INVALIDARG;

    VARIANT_BOOL vbIsDirectory = VARIANT_FALSE;
    RETURN_FAILED(newVal->isDirectory(&vbIsDirectory))
    if (VARIANT_FALSE == vbIsDirectory)
        return E_INVALIDARG;

    CComBSTR bsPath;
    RETURN_FAILED(newVal->get_nativePath(&bsPath))

    m_strDisplayDirectory = bsPath;

    return S_OK;
}

STDMETHODIMP CFilePicker::get_filterIndex(LONG* pVal)
{
    if (!pVal)
        return E_POINTER;

    *pVal = m_nFilterIndex;

    return S_OK;
}

STDMETHODIMP CFilePicker::put_filterIndex(LONG newVal)
{
    m_nFilterIndex = newVal;

    return S_OK;
}
